#include "index_manager.h"
#include <iostream>

IndexManager::IndexManager()
{}

IndexManager::~IndexManager()
{}

int IndexManager::LoadDB(std::string &db)
{
    dbPath_ = db;

    int ret = sqlite3_open(dbPath_.c_str(), &db_);
    if (ret != SQLITE_OK) {
        std::cout << "open " << db << " fail\n";
        return -1;
    }

    sqlite3_exec(db_,
                 "CREATE TABLE IF NOT EXISTS indexs (" \
                 "id         INTEGER PRIMARY KEY NOT NULL," \
                 "token      TEXT                NOT NULL UNIQUE," \
                 "doc_count  INT                 NOT NULL ," \
                 "postings   BLOB                NOT NULL" \
                 ");",
                 NULL, NULL, NULL);

    sqlite3_prepare(db_, "SELECT id FROM indexs WHERE token = ?;", -1, &getTokenID_, NULL);
    sqlite3_prepare(db_, "SELECT doc_count, postings FROM indexs WHERE token = ?;", -1, &getPosting_, NULL);
    sqlite3_prepare(db_, "INSERT OR IGNORE INTO indexs (token, doc_count, postings) VALUES (?, ?, ?);", -1, &insertPosting_, NULL);
    sqlite3_prepare(db_, "UPDATE indexs SET doc_count = ?, postings = ? WHERE token = ?;", -1, &updatePosting_, NULL);

    return 0;
}

PostingList IndexManager::GetPosting(std::string &token)
{
    PostingList lst(token);
    sqlite3_stmt *st = getPosting_;

    sqlite3_reset(st);
    sqlite3_bind_text(st, 1, token.c_str(), token.size(), SQLITE_STATIC);

    int ret = sqlite3_step(st);
    if (ret == SQLITE_ROW) {
        int occur = sqlite3_column_int(st, 0);
        unsigned char *buffer = (unsigned char *)sqlite3_column_blob(st, 1);
        int size = (int)sqlite3_column_bytes(st, 1);

        int ret = lst.Decode(buffer, size);
        if (ret != 0) {
            std::cout << "decode " << token << " fail\n";
            return lst;
        }
    }

    return lst;
}

int IndexManager::WriteList(PostingList &posting)
{
    unsigned char *buf = nullptr;
    int size = 0;
    std::string token = posting.GetToken();
    sqlite3_stmt *st;

    posting.Encode(&buf, size);

    if (GetTokenID(posting.GetToken()) == 0) {
        st = insertPosting_;
        sqlite3_reset(st);
        sqlite3_bind_text(st, 1, token.c_str(), token.size(), SQLITE_STATIC);
        sqlite3_bind_int(st, 2, posting.GetDocCount());
        sqlite3_bind_blob(st, 3, buf, size, SQLITE_STATIC);
    } else {
        st = updatePosting_;
        sqlite3_reset(st);
        sqlite3_bind_int(st, 1, posting.GetDocCount());
        sqlite3_bind_blob(st, 2, buf, size, SQLITE_STATIC);
        sqlite3_bind_text(st, 3, token.c_str(), token.size(), SQLITE_STATIC);
    }

    int ret = sqlite3_step(st);
    if (ret != SQLITE_DONE) {
        std::cout << "write index " << token << " fail: " << sqlite3_errmsg(db_) << "\n";
        return -1;
    }

    return 0;
}

int IndexManager::GetTokenID(std::string token)
{
    sqlite3_stmt *st = getTokenID_;
    sqlite3_reset(st);
    sqlite3_bind_text(st, 1, token.c_str(), token.size(), SQLITE_STATIC);

    int ret = sqlite3_step(st);
    if (ret == SQLITE_ROW) {
        return sqlite3_column_int(st, 0);
    } else {
        return 0;
    }
}
